/*****************************************************
	ShiftRegister.c Joe Pardue September 8, 2010
******************************************************/

// I HATE LICENSES LIKE THIS >>BUT<< I've been told that without
// the license then the work is automatically copyrighted in my name
// since my purpose is to educate (and learn), I want the code to be 
// used by whoever wants to use it to learn something. If you like it, 
// then visit my website www.smileymicros.com and buy something.

/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *   
 *  - Neither the name of the Smiley Micros nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// And to further cover my butt, let me add that if you use this software
// it will destroy whatever machine you use it on and kill anyone in a one 
// kilometer radius. So don't even consider using it for any reason whatsoever!



/*****************************************************
	To use this source code you must select the device
	in the #define list AND you must set the processor
	for the device in the Configuration Options
	
	Select the device from the #define list:
	Remove '//' from device you are using
	Add '//' to devices you are not using

	Select the processor from Projects menu item 
	Configuration Options - Device:
	Butterfly - ATmega169
	Arduino - ATmega328
	BeAVR - ATmega644

	You must make both selections for this to compile
	properly in AVRStudio
******************************************************/

//#define Butterfly // ATmega169
//#define Arduino // ATmega328
#define BeAVR40 // ATmega644

/*****************************************************
	And you'll want an avrdude script for each:

	avrdude -p m169 -c butterfly -P com4 -b 19200 -F -U flash:w:ShiftRegister.hex
	avrdude -p m328p -c arduino -P com3 -b 57600 -F -U flash:w:ShiftRegister.hex
	avrdude -p m644p -c avr109 -P com3 -b 57600 -F -U flash:w:ShiftRegister.hex
	
	Just remember that in the Command Prompt window to 
	make sure you are in the same directory as hex file 
	and that you have changed the comX port to your port.
	and that you are using the correct baud rate
******************************************************/


// SmileyUSART.h uses the above defines
// Note that Butterfly USART runs at 19200 Baud
// while the Arduino and BeAVR40 run at 57600 Baud
#include "SmileyUSART.h"
#include <util\delay.h> // requires F_CPU in SmileyUSART.h

#if defined(Arduino)
const char HELLO[] PROGMEM = "ShiftRegiser - Arduino 1.02\r";
#elif defined (Butterfly)
const char HELLO[] PROGMEM = "ShiftRegiser - Butterfly 1.18\r";
#elif defined (BeAVR40)
const char HELLO[] PROGMEM = "ShiftRegiser - BeAVR40 1.00\r";
#else
const char HELLO[] PROGMEM = "ShiftRegiser - UNDEFINED\r";
#endif

// Move bitwise operations to macros to 
// help keep folks heads from exploding
#define clear_ss() ss_port &= ~(1<<ss_port_pin)
#define set_ss() ss_port |= (1<<ss_port_pin) 
#define set_mosi_bit() mosi_port |= (1<<mosi_port_pin)
#define clear_mosi_bit() mosi_port &= ~(1<<mosi_port_pin)
#define is_bit_set(myVar, i) (!!(myVar & (1 << i)))

// Return the data pin state
uint8_t get_miso_bit(void);

// Send 0 or 1
void uart_send_bit(uint8_t);

// Function loops forever in main()
void loop(void);

// Initialize the I/O pins
void setup_pins(void);

// Return the data pin state
uint8_t get_miso_pin(void);

// Turn the clock line 0 to 1
void toggle_clock(void);

// Define ports, pins, and data direction registers for set up
#define mosi_port PORTB
#define mosi_port_pin  PORTB2
#define mosi_ddr  DDRB
#define mosi_port_pins PINB

#define miso_port PORTB
#define miso_port_pin  PORTB1
#define miso_ddr  DDRB
#define miso_port_pins PINB

#define sclk_port PORTD
#define sclk_port_pin PORTD7
#define sclk_ddr DDRD

#define ss_port PORTB
#define ss_port_pin  PORTB0
#define ss_ddr  DDRB

int main(void)
{
	
	Initialization();

	sendStringP(HELLO);

	setup_pins();
	
	while(1)
	{
	  loop();
	}
	
}


/*
To create a 16 LED Cylon Eye effect
output this array 0 to 15 then 15 to 0
0000 0000 0000 0001 == 0x0001
0000 0000 0000 0010 == 0x0002
0000 0000 0000 0100 == 0x0004
0000 0000 0000 1000 == 0x0008
0000 0000 0001 0000 == 0x0010
0000 0000 0010 0000 == 0x0020
0000 0000 0100 0000 == 0x0040
0000 0000 1000 0000 == 0x0080
0000 0001 0000 0000 == 0x0100
0000 0010 0000 0000 == 0x0200
0000 0100 0000 0000 == 0x0400
0000 1000 0000 0000 == 0x0800
0001 0000 0000 0000 == 0x1000
0010 0000 0000 0000 == 0x2000
0100 0000 0000 0000 == 0x4000
1000 0000 0000 0000 == 0x8000

*/

void loop()
{
	int i,j;
  	uint8_t my_data_in = 0;

	uint16_t ce[] = { 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 };
    
  	// load 8 bits from the 597 into my_data_in byte
  	for (i=7; i>=0; i--)
  	{
    	// Toggle the clock to get the next bit
    	toggle_clock();

    	// If data pin = 1 set it in my_data otherwise
    	// do nothing since my_data_in is initialized to 0
    	if (get_miso_bit()){//temp) {
    		my_data_in |= (1 << i);
			uart_send_bit(1);
    	}
		else
		{
			uart_send_bit(0);
		}
  	}

  	// Demark each read
  	sendStringP(PSTR("\r>----------<\r"));

	// Sweep right to left
  	for (j=0; j<15; j++)  // RS changed <= to <
  	{
  		// Clear /SS so data won't show while shifting
 		//ss_port &= ~(1<<ss_port_pin);
		clear_ss();

  		// output 16 bits to the 555
  		for (i=0; i<=15; i++)
  		{
			// Put bit on mosi_pin
			if( is_bit_set(ce[j], i) )//!!(ce[j] & (1 << i)))
				set_mosi_bit();
				//mosi_port |= (1<<mosi_port_pin);
			else
				clear_mosi_bit();//mosi_port &= ~(1<<mosi_port_pin);

    		// Toggle the clock to output it
    		toggle_clock();
  		}

  		// Set /SS to transfer data from serial to parallel registers
  		//ss_port |= (1<<ss_port_pin);
		set_ss();

  		// Wait a while
  		_delay_ms(100);
  	}

  	// Now sweep left to right
  	for (j=15; j>0; j--)	// RS changed <= to <
  	{

  		// Clear /SS so data won't show while shifting
 		//ss_port &= ~(1<<ss_port_pin);
		clear_ss();

  		// output 16 bits to the 585
  		for (i=0; i<=15; i++)
  		{

			// Put bit on mosi_pin
			if( is_bit_set(ce[j], i) )
				set_mosi_bit();
			else
				clear_mosi_bit();

    		// Toggle the clock to output it
    		toggle_clock();

  		}
  		// Set /SS to transfer data from serial to parallel registers
  		//ss_port |= (1<<ss_port_pin);
		set_ss();

		// Wait a while
  		_delay_ms(100);
  	}
}





void setup_pins()
{
  	// Initialize MISO as input
  	// set DDR pin register to 0
  	miso_ddr &= ~(1<<miso_port_pin);

  	// Initialize MOSI, SCLK, AND /SS as outputs
  	// set DDR pin registers to 1
    	mosi_ddr |= (1<<mosi_port_pin);
	sclk_ddr |= (1<<sclk_port_pin);
  	ss_ddr |= (1<<ss_port_pin); 

/*  	// Initialize data in as input
  	// set DDR pin register to 0
  	miso_ddr &= ~(1<<miso_port_pin);

  	// Initialize data out as output
  	// set DDR pin registers to 1
  	mosi_ddr |= (1<<mosi_port_pin);

  
  	// Initialize Clock, Latches, and Load as outputs
  	// set DDR pin registers to 1
  	sclk_ddr |= (1<<sclk_port_pin);
  	ss_ddr |= (1<<ss_port_pin);
 */ 
}

// Return the data pin state
uint8_t get_miso_bit()
{
   if (miso_port_pins & (1<<miso_port_pin))
   {
     return 1;
   }
   else return 0;
}

// Turn the clock line 0 to 1
void toggle_clock()
{
   sclk_port &= ~(1<<sclk_port_pin);
   _delay_ms(0.2);
   sclk_port |= (1<<sclk_port_pin); 
}







/*****************************************************
	USART Functions
******************************************************/
void USARTInit()
{
	// Set Baud registers with values from <util\setbaud.h> in SmileyUSART.h
	USART_BAUD_RATE_HIGH = (F_CPU/(BAUD*16L)-1) >> 8;
	USART_BAUD_RATE_LOW = (uint8_t)(F_CPU/(BAUD*16L)-1);

	// Enable receiver and transmitter
	USART_CONTROL_STATUS_REG_B = (1 << USART_ENABLE_RECEIVER) | (1 << USART_ENABLE_TRANSMITTER);
	// Set frame format: n,8,1
#if defined(BeAVR40) || defined(Arduino)
	USART_CONTROL_STATUS_REG_C |= (1 << USART_STOP_BIT_SELECT) | (1 << USART_CHARACTER_SIZE);  
#elif defined(ATmega32)
	USART_CONTROL_STATUS_REG_C |= (1 << USART_REGISTER_SELECT) | (1 << USART_CHARACTER_SIZE_0) | (1 << USART_CHARACTER_SIZE_1);

#elif defined(Butterfly)
	// for the Butterfly reuse the Atmel code for the USART in the intialization process
#else
   #error "No USART definition for MCU in USARTInit"
#endif 
}

/*uint8_t receiveByte( void )
{
	wdt_reset(); // reset the watchdog timer, if it is set
	// Wait for data to be received 
	while ( !(USART_CONTROL_STATUS_REG_A & (1<<USART_RECEIVE_COMPLETE)) );	
	// Get and return received data from buffer 
	return USART_DATA_REG;
}*/

void sendByte( uint8_t data )
{
	wdt_reset(); // reset the watchdog timer, if it is set
	// Wait for empty transmit buffer 
	while ( !( USART_CONTROL_STATUS_REG_A & (1<<USART_READY_TO_TRANSMIT)) );
	// Put data into buffer, sends the data
	USART_DATA_REG = data;
}

void uart_send_bit( uint8_t data )
{
	wdt_reset(); // reset the watchdog timer, if it is set
	// Wait for empty transmit buffer 
	while ( !( USART_CONTROL_STATUS_REG_A & (1<<USART_READY_TO_TRANSMIT)) );
	
	if(data == 1)
	{
		USART_DATA_REG = '1';
	}
	else 
	{
		USART_DATA_REG = '0';
	}
}

void sendStringP(const char *FlashString)
{
	int i = 0;

	// The 'for' logic terminates if the byte is '\0' or if i = 80.
	// '\0' is 'null' and terminates C strings
	// The 80 prevents too much overrun if we get a bad pointer
	// and it limits the string size	
	for( i = 0 ; pgm_read_byte(&FlashString[i]) && i < 80; i++) 
	{
			sendByte(pgm_read_byte(&FlashString[i]));
	}
}

/*****************************************************
	Initialization
******************************************************/

void Initialization()
{
	USARTInit();

#if defined(Butterfly)
// NOTE this all copied directly from the original Butterfly source code
// so it uses a different method for naming registers than the rest of the code
// updated register names to fit new io headers - why do they keep changing this?

    OSCCAL_calibration();   // Calibrate the OSCCAL byte

    ACSR = (1<<ACD);

    // Disable Digital input on PF0-2 (power save)
    DIDR1 = (7<<ADC0D);

    PORTB = 0xFF;       // Enable pullups on Ports B and E
    PORTE = 0xFF;
    
    // boost IntRC to 2Mhz to achieve 19200 baudrate
    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 4, Inter RC 8Mhz / 4 = 2Mhz
    CLKPR = (1<<CLKPS1);    

    UBRRH = 0;//(unsigned char)(baudrate>>8);
    UBRRL = 12;//(unsigned char)baudrate;

    // Enable 2x speed
    UCSRA = (1<<U2X);

    // Enable receiver and transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN)|(0<<RXCIE)|(0<<UDRIE);

    // Async. mode, 8N1
    UCSRC = (0<<UMSEL)|(0<<UPM1)|(0<<USBS)|(3<<UCSZ0)|(0<<UCPOL);
        
    MCUCR = (1<<IVCE);       
    MCUCR = (1<<IVSEL);             //move interruptvectors to the Boot sector    

    sei(); // mt __enable_interrupt();
    
    // mt PCMSK1 = (1<<PORTB6) | (1<<PORTB4);
	PCMSK1 = (1<<PINB6) | (1<<PINB4);       // set pin-change interrupt mask
  	EIFR   = (1<<PCIF1);                    // clear external intterupt flag 1
	EIMSK  = (1<<PCIE1);   

#endif
}


// Only compile if Butterfly is defined
#if defined(Butterfly)
/*****************************************************
	Oscillator Calibration
	The following code is taken directly from Martin Thoma's
	port of Atmel's IAR Butterfly software to GCC
******************************************************/
/*****************************************************************************
*
*   Function name : OSCCAL_calibration
*
*   Returns :       None
*
*   Parameters :    None
*
*   Purpose :       Calibrate the internal OSCCAL byte, using the external 
*                   32,768 kHz crystal as reference
*
*****************************************************************************/
void OSCCAL_calibration(void)
{
    unsigned char calibrate = 0;//FALSE;
    int temp;
    unsigned char tempL;

    CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
    // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz
    CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
    
    TIMSK2 = 0;             //disable OCIE2A and TOIE2

    ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
    
    OCR2A = 200;            // set timer2 compare value 

    TIMSK0 = 0;             // delete any interrupt sources
        
    TCCR1B = (1<<CS10);     // start timer1 with no prescaling
    TCCR2A = (1<<CS20);     // start timer2 with no prescaling

    while((ASSR & 0x01) | (ASSR & 0x04));       //wait for TCN2UB and TCR2UB to be cleared

    Delay(1000);    // wait for external crystal to stabilise
    
    while(!calibrate)
    {
        cli(); // mt __disable_interrupt();  // disable global interrupt
        
        TIFR1 = 0xFF;   // delete TIFR1 flags
        TIFR2 = 0xFF;   // delete TIFR2 flags
        
        TCNT1H = 0;     // clear timer1 counter
        TCNT1L = 0;
        TCNT2 = 0;      // clear timer2 counter
           
	while ( !(TIFR2 & (1<<OCF2A)) ); // while ( !(TIFR2 && (1<<OCF2A)) );   // wait for timer2 compareflag
    
        TCCR1B = 0; // stop timer1

        sei(); // __enable_interrupt();  // enable global interrupt
    
        if ( (TIFR1 & (1<<TOV1)) ) // if ( (TIFR1 && (1<<TOV1)) )
        {
            temp = 0xFFFF;      // if timer1 overflows, set the temp to 0xFFFF
        }
        else
        {   // read out the timer1 counter value
            tempL = TCNT1L;
            temp = TCNT1H;
            temp = (temp << 8);
            temp += tempL;
        }
    
        if (temp > 6250)
        {
            OSCCAL--;   // the internRC oscillator runs to fast, decrease the OSCCAL
        }
        else if (temp < 6120)
        {
            OSCCAL++;   // the internRC oscillator runs to slow, increase the OSCCAL
        }
        else
            calibrate = 1;//TRUE;   // the interRC is correct
    
        TCCR1B = (1<<CS10); // start timer1
    }
}


/*****************************************************************************
*
*   Function name : Delay
*
*   Returns :       None
*
*   Parameters :    unsigned int millisec
*
*   Purpose :       Delay-loop
*
*****************************************************************************/
void Delay(unsigned int millisec)
{
    unsigned char i; // mt int i;
    
    while (millisec--)
        for (i=0; i<125; i++);
}

#endif
